package psn.zjk.exporter;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.poi.xssf.usermodel.XSSFCellStyle;
import psn.zjk.exporter.utils.PojoEnhancer;

import java.text.MessageFormat;
import java.util.*;

/**
 * @author sdyt_zhangjunkai
 * @version 1.0
 * @CLASS_NAME ExcelDataBuilder
 * @DESCRIPTION 数据详情：列头、列头、样式等
 * @CREATE_TIME 2020/9/1 16:20
 */
public abstract class ExcelDataBuilder {

    /**
     * 填充标记
     */
    protected ExcelData excelData;

    protected boolean sortRow;
    protected boolean sortColumn;
    protected Comparator rowComparator;
    protected Comparator columnComparator;
    protected boolean mergeRepeat;

    protected Map<String, SheetRow> uniqueRows = new HashMap<>(1024);
    protected Map<String, SheetColumn> uniqueHeaders = new HashMap<>(32);

    /**
     * 存储样式（共用），表外数据（头不参与排序）
     */
    protected Map<Integer, SheetRow> tmpRows = new HashMap<>(1024);
    protected Map<Integer, SheetColumn> tmpColumns = new HashMap<>(32);

    public ExcelDataBuilder() {
        excelData = new ExcelData();
        excelData.firstRow = 0;
        excelData.firstColumn = 0;
        excelData.firstDataRowIndex = 1;
        excelData.firstDataColumnIndex = 1;
    }

    /**
     * @param rowHeaderField
     * @param headerField
     * @param valueField
     * @METHOD_NAME: newBuilder
     * @DESCRIPTION:
     * @CREATE_TIME 2020/9/10 9:44
     * @RETURN com.citicbank.fmut.infrastructure.util.excel.ExcelDataBuilder
     * @AUTHOR sdyt_zhangjunkai
     */
    public static ExcelDataBuilder newBuilder(String rowHeaderField, String headerField, String valueField) {
        return new DynamicTableBuilder(rowHeaderField, headerField, valueField);
    }

    /**
     * @param fixedRowHeaders
     * @param headerField
     * @METHOD_NAME: newBuilder
     * @DESCRIPTION: 固定行头
     * @CREATE_TIME 2020/9/10 9:43
     * @RETURN com.citicbank.fmut.infrastructure.util.excel.ExcelDataBuilder
     * @AUTHOR sdyt_zhangjunkai
     */
    public static ExcelDataBuilder newBuilder(List<String> fixedRowHeaders, String headerField) {
        return new FixedRowHeaderTableBuilder(fixedRowHeaders, headerField);
    }

    /**
     * @param rowHeaderField
     * @param fixedHeaders
     * @METHOD_NAME: newBuilder
     * @DESCRIPTION: 固定列头
     * @CREATE_TIME 2020/9/10 9:43
     * @RETURN com.citicbank.fmut.infrastructure.util.excel.ExcelDataBuilder
     * @AUTHOR sdyt_zhangjunkai
     */
    public static ExcelDataBuilder newBuilder(String rowHeaderField, List<String> fixedHeaders) {
        return new FixedHeaderTableBuilder(rowHeaderField, fixedHeaders);
    }


    /**
     * @param rowIndex    列头第一个单元格的行坐标
     * @param columnIndex 列头第一个单元格的列坐标
     * @METHOD_NAME: setHeaderCoordinate
     * @DESCRIPTION: 确定列头位置
     * @CREATE_TIME 2020/9/10 9:41
     * @RETURN com.citicbank.fmut.infrastructure.util.excel.ExcelDataBuilder
     * @AUTHOR sdyt_zhangjunkai
     */
    public ExcelDataBuilder setHeaderCoordinate(int rowIndex, int columnIndex) {
        excelData.firstRow = rowIndex;
        excelData.firstDataColumnIndex = columnIndex;
        return this;
    }

    /**
     * @param rowIndex    行头第一个单元格的行坐标
     * @param columnIndex 行头第一个单元格的列坐标
     * @METHOD_NAME: setRowHeaderCoordinate
     * @DESCRIPTION:
     * @CREATE_TIME 2020/9/10 9:40
     * @RETURN com.citicbank.fmut.infrastructure.util.excel.ExcelDataBuilder
     * @AUTHOR sdyt_zhangjunkai
     */
    public ExcelDataBuilder setRowHeaderCoordinate(int rowIndex, int columnIndex) {
        excelData.firstDataRowIndex = rowIndex;
        excelData.firstColumn = columnIndex;
        return this;
    }

    /**
     * @param datas
     * @METHOD_NAME: setDatas
     * @DESCRIPTION: 填充数据，可能的异常来自于从数据中提取属性出错
     * @CREATE_TIME 2020/9/4 15:17
     * @RETURN void
     * @AUTHOR sdyt_zhangjunkai
     */
    public ExcelDataBuilder setDatas(List<?> datas) {
        excelData.datas = datas;
        return this;
    }

    public ExcelData build() {
        //初始化列头、列头
        SheetColumn column = column(excelData.firstColumn);
        SheetRow row = row(excelData.firstRow);
        initRowHeaders(column);
        initHeaders(row);
        //初始化数据区单元格
        initCells();
        //组装数据
        if (!CollectionUtils.isEmpty(excelData.datas)) {
            assembleDatas();
        }
        if (tmpColumns.size() > 0) {
            assembleTmpColumns();
        }
        if (tmpRows.size() > 0) {
            assembleTmpRows();
        }
        return excelData;
    }

    protected void assembleTmpRows() {
        for (Map.Entry<Integer, SheetRow> entry : tmpRows.entrySet()) {
            Integer rowIndex = entry.getKey();
            SheetRow tmpRow = entry.getValue();
            for (Integer columnIndex : excelData.sheetColumns.keySet()) {
                cell(rowIndex, columnIndex);
            }

            SheetRow row = row(rowIndex);
            ExporterUtils.copyTo(tmpRow, row);


            //普通数据
            List<Object> cells = row.getCells();
            if (cells != null && cells.size() > 0) {
                boolean first = true;
                //数据
                int columnIndex = excelData.firstDataColumnIndex;
                for (Object cell : cells) {
                    if (first) {//头
                        cell(rowIndex, excelData.firstColumn, cell);
                        first = false;
                        continue;
                    }
                    cell(rowIndex, columnIndex++, cell);
                }
            }

            //Pojo数据
            Object pojo = row.getPojo();
            if (pojo != null) {
                //头
                cell(rowIndex, excelData.firstColumn, row.getShownHeader());
                //数据
                assembleTmpPojo(row, pojo);
            }
        }
    }

    protected void assembleTmpColumns() {
        for (Map.Entry<Integer, SheetColumn> entry : tmpColumns.entrySet()) {//列
            Integer columnIndex = entry.getKey();
            SheetColumn tmpColumn = entry.getValue();

            for (Integer rowIndex : excelData.sheetRows.keySet()) {
                cell(rowIndex, columnIndex);
            }

            SheetColumn sheetColumn = column(columnIndex);
            ExporterUtils.copyTo(tmpColumn, sheetColumn);

            //普通数据
            List<Object> cells = tmpColumn.getCells();
            if (cells != null && cells.size() > 0) {
                boolean first = true;
                //数据
                int rowIndex = excelData.firstDataRowIndex;
                for (Object cell : cells) {
                    if (first) {//头
                        cell(excelData.firstRow, columnIndex, cell);
                        first = false;
                        continue;
                    }
                    cell(rowIndex++, columnIndex, cell);
                }
            }

            //pojo数据
            Object pojo = tmpColumn.getPojo();
            if (pojo != null) {
                //头
                cell(excelData.firstRow, columnIndex, sheetColumn.getShownHeader());
                //数据
                assembleTmpPojo(sheetColumn, pojo);
            }
        }
    }

    protected void assembleTmpPojo(SheetRow row, Object pojo) {
        throw new UnsupportedOperationException("Not supported method!");
    }

    protected void assembleTmpPojo(SheetColumn column, Object pojo) {
        throw new UnsupportedOperationException("Not supported method!");
    }


    protected void initRowHeaders(SheetColumn column) {
        List<String> list = getRowHeaders();
        if (sortRow) {
            Collections.sort(list);
        }
        SheetColumn tmpPojoColumn = tmpColumns.get(excelData.firstRow);
        if (tmpPojoColumn == null) {
            list.forEach(s -> {
                SheetRow row = row(s);
                String header = excelData.rowHeaderField == null ? s : excelData.rowHeaderField;
                row.setHeader(header);
                s = getMappedShownHeaderOfRow(s);
                cell(row, column, s);
            });
        } else {
            list.forEach(s -> {
                SheetRow row = row(s);
                String header = excelData.rowHeaderField == null ? s : excelData.rowHeaderField;
                row.setHeader(header);
                s = getMappedShownHeaderOfRow(s);
                cell(row, column, s, tmpPojoColumn.getStyle()).setBelongedData(tmpPojoColumn.getPojo());
            });
        }
    }

    //初始化列头、列头
    protected void initHeaders(SheetRow row) {
        List<String> list = getHeaders();
        if (sortRow) {
            Collections.sort(list);
        }
        SheetRow tmpPojoRow = tmpRow(excelData.firstRow);
        if (tmpPojoRow == null) {
            list.forEach(s -> {
                SheetColumn column = column(s);
                String header = excelData.headerField == null ? s : excelData.headerField;
                column.setHeader(header);
                s = getMappedShownHeaderOfColumn(s);
                cell(row, column, s);
            });
        } else {
            list.forEach(s -> {
                SheetColumn column = column(s);
                String header = excelData.headerField == null ? s : excelData.headerField;
                column.setHeader(header);
                s = getMappedShownHeaderOfColumn(s);
                cell(row, column, s, tmpPojoRow.getStyle()).setBelongedData(tmpPojoRow.getPojo());
            });
        }
    }

    protected abstract List<String> getHeaders();

    protected abstract List<String> getRowHeaders();

    /**
     * @param
     * @METHOD_NAME: initCells
     * @DESCRIPTION: 表格区域内，创建所有的单元格，赋予默认样式
     * @CREATE_TIME 2020/9/18 9:24
     * @RETURN void
     * @AUTHOR sdyt_zhangjunkai
     */
    protected void initCells() {
        int rowSize = excelData.sheetRows.size();
        int columnSize = excelData.sheetColumns.size();
//        SheetRow firstRow = row(excelData.firstRow);
//        SheetColumn firstColumn = column(excelData.firstColumn);
//        //行头
//        for (int i = excelData.firstRow; i < excelData.firstRow + rowSize; i++) {
//            SheetCell cell = cell(i, excelData.firstColumn, firstColumn.getShownHeader());
//            XSSFCellStyle style = firstColumn.getStyle();
//            if (style != null) {
//                cell.setStyle(style);
//            }
//        }
//        //列头
//        for (int i = excelData.firstColumn; i < excelData.firstColumn + columnSize; i++) {
//            SheetCell cell = cell(excelData.firstRow, i, firstRow.getShownHeader());
//            XSSFCellStyle style = firstRow.getStyle();
//            if (style != null) {
//                cell.setStyle(style);
//            }
//        }
        //数据
        for (int i = excelData.firstDataRowIndex; i < excelData.firstDataRowIndex + rowSize - 1; i++) {
            for (int j = excelData.firstDataColumnIndex; j < excelData.firstDataColumnIndex + columnSize - 1; j++) {
                SheetCell cell = cell(i, j);
                if (cell.getStyle() == null) {
                    cell.setStyle(excelData.defaultCellStyle);
                }
            }
        }

    }

    protected abstract void assembleDatas();

    public ExcelDataBuilder insertRow(int rowIndex, List<Object> cells) {
        return insertRow(rowIndex, cells, null);
    }

    /**
     * @param rowIndex    起始单元格行坐标
     * @param cells
     * @param style
     * @METHOD_NAME: insertRow
     * @DESCRIPTION: 插入行
     * @CREATE_TIME 2020/8/21 16:27
     * @RETURN com.citicbank.fmut.position.base.domain.step.ExcelSheetBuilder
     * @AUTHOR sdyt_zhangjunkai
     */
    public ExcelDataBuilder insertRow(int rowIndex, List<Object> cells, XSSFCellStyle style) {
        SheetRow sheetRow = tmpRow(rowIndex);
        sheetRow.setStyle(style);
        sheetRow.setCells(cells);
        return this;
    }

    /**
     * @param columnIndex
     * @param cells
     * @param style
     * @METHOD_NAME: insertColumn
     * @DESCRIPTION: 插入列附带样式
     * @CREATE_TIME 2020/9/4 14:40
     * @RETURN com.citicbank.fmut.web.excel.core.ExcelSheetBuilder
     * @AUTHOR sdyt_zhangjunkai
     */
    public ExcelDataBuilder insertColumn(int columnIndex, List<Object> cells, XSSFCellStyle style) {
        SheetColumn sheetColumn = tmpColumn(columnIndex);
        sheetColumn.setStyle(style);
        sheetColumn.setCells(cells);
        return this;
    }

    public Object getPropertyWithConvertNull(Object pojo, String property) {
        Object value = PojoEnhancer.getProperty(pojo, property);
        //null转空字符串，避免显示null
        value = value == null ? "" : value;
        return value;
    }


    protected SheetCell cell(int rowIndex, int columnIndex) {
        return cell(rowIndex, columnIndex, null);
    }

    protected SheetCell cell(int rowIndex, int columnIndex, Object value, XSSFCellStyle style) {
        SheetCell cell = cell(rowIndex, columnIndex, value);
        if (style != null) {
            cell.setStyle(style);
        }
        return cell;
    }

    protected SheetCell cell(SheetRow row, SheetColumn column, Object value, XSSFCellStyle style) {
        return cell(row.getIndex(), column.getIndex(), value, style);
    }

    protected SheetCell cell(SheetRow row, SheetColumn column, Object value) {
        return cell(row.getIndex(), column.getIndex(), value);
    }

    /**
     * @METHOD_NAME: cell
     * @DESCRIPTION: 生成一个单元格，加入到行、列、全局数据中,不支持设置value、style为null
     * 需要置空，先拿到单元格对象
     * @CREATE_TIME 2020/9/3 20:43
     * @RETURN com.citicbank.fmut.web.excel.SheetCell
     * @AUTHOR sdyt_zhangjunkai
     */
    protected SheetCell cell(int rowIndex, int columnIndex, Object value) {
        long coordinate = getCoordinate(rowIndex, columnIndex);
        SheetCell sheetCell = excelData.assembledDatas.get(coordinate);
        if (sheetCell == null) {
            SheetRow sheetRow = row(rowIndex);
            SheetColumn sheetColumn = column(columnIndex);
            sheetCell = new SheetCell(sheetRow, sheetColumn);
            //自动填入头样式
            if (rowIndex == excelData.firstRow) {
                sheetCell.setStyle(sheetRow.getStyle());
            } else if (columnIndex == excelData.firstColumn) {
                sheetCell.setStyle(sheetColumn.getStyle());
            }
            excelData.assembledDatas.put(coordinate, sheetCell);
        }
        if (value != null) {
            sheetCell.setValue(value);
        }
        return sheetCell;
    }

    protected SheetColumn column(int columnIndex) {
        SheetColumn sheetColumn = excelData.sheetColumns.get(columnIndex);
        if (sheetColumn == null) {
            sheetColumn = new SheetColumn(columnIndex);
            sheetColumn.setHeader(excelData.headerField);
            excelData.sheetColumns.put(columnIndex, sheetColumn);
        }

        return sheetColumn;
    }

    protected SheetColumn column(int newIndex, String newShownHeader) {
        int index = -1;
        if (uniqueHeaders.containsKey(newShownHeader) && (index = uniqueHeaders.get(newShownHeader).getIndex()) != newIndex) {
            throw new IllegalArgumentException(MessageFormat.format("列[{0}]、列[{0}]，显示名冲突", index, newIndex));
        }
        SheetColumn column = column(newIndex);
        String oldShownHeader = column.getShownHeader();
        if (oldShownHeader != null) {//移除老行
            uniqueHeaders.remove(oldShownHeader);
        }
        if (newShownHeader != null) {//放入新行
            column.setShownHeader(newShownHeader);
            uniqueHeaders.put(newShownHeader, column);
        }
        return column;
    }

    protected String getMappedShownHeaderOfRow(String shownHeader) {
        if (excelData.rowHeaderMapping != null && excelData.rowHeaderMapping.containsKey(shownHeader)) {
            shownHeader = excelData.rowHeaderMapping.get(shownHeader);
        }
        return shownHeader;
    }

    protected String getMappedShownHeaderOfColumn(String shownHeader) {
        if (excelData.headerMapping != null && excelData.headerMapping.containsKey(shownHeader)) {
            shownHeader = excelData.headerMapping.get(shownHeader);
        }
        return shownHeader;
    }

    protected SheetRow row(int rowIndex) {
        SheetRow sheetRow = excelData.sheetRows.get(rowIndex);
        if (sheetRow == null) {
            sheetRow = new SheetRow(rowIndex);
            sheetRow.setHeader(excelData.rowHeaderField);
            excelData.sheetRows.put(rowIndex, sheetRow);
        }
        return sheetRow;
    }

    protected SheetRow row(int rowIndex, String newShownHeader) {
        int index = -1;
        if (uniqueRows.containsKey(newShownHeader) && (index = uniqueRows.get(newShownHeader).getIndex()) != rowIndex) {
            throw new IllegalArgumentException(MessageFormat.format("行[{0}]、行[{0}]，显示名冲突", index, rowIndex));
        }
        SheetRow row = row(rowIndex);
        String oldShownHeader = row.getShownHeader();

        if (oldShownHeader != null) {//移除老行
            uniqueRows.remove(oldShownHeader);
        }
        if (newShownHeader != null) {//放入新行
            row.setShownHeader(newShownHeader);
            uniqueRows.put(newShownHeader, row);
        }

        return row;
    }

    public ExcelDataBuilder setSortRow(boolean sortRow) {
        this.sortRow = sortRow;
        return this;
    }

    public ExcelDataBuilder setSortColumn(boolean sortColumn) {
        this.sortColumn = sortColumn;
        return this;
    }

    public ExcelDataBuilder setRowComparator(Comparator rowComparator) {
        this.rowComparator = rowComparator;
        return this;
    }

    public ExcelDataBuilder setColumnComparator(Comparator columnComparator) {
        this.columnComparator = columnComparator;
        return this;
    }

    /**
     * 列头排序
     */
    protected ExcelDataBuilder sortHeaders() {
        if (excelData.sheetColumns.size() > 3) {
            SheetColumn firstColumn = excelData.sheetColumns.remove(excelData.firstColumn);
            //行头不参与排序
            List<SheetColumn> sheetColumnList = new ArrayList<>(excelData.sheetColumns.values());
            if (columnComparator == null) {
                sheetColumnList.sort((data1, data2) -> {
                    String shownHeader1 = data1.getShownHeader();
                    String shownHeader2 = data2.getShownHeader();
                    if (shownHeader1 == null || shownHeader2 == null) {
                        return 0;
                    }
                    return shownHeader1.compareTo(shownHeader2);
                });
            } else {
                sheetColumnList.sort(columnComparator);
            }
            int index = excelData.firstDataColumnIndex;
            excelData.sheetColumns.clear();
            for (SheetColumn sheetColumn : sheetColumnList) {
                sheetColumn.setIndex(index);
                excelData.sheetColumns.put(index, sheetColumn);
                index++;
            }
            excelData.sheetColumns.put(excelData.firstColumn, firstColumn);
        }
        return this;
    }

    /**
     * @param headerMapping 映射：原列头—列头
     * @METHOD_NAME: setAliasHeaders
     * @DESCRIPTION: 设置列头别名
     * @CREATE_TIME 2020/9/4 14:49
     * @RETURN void
     * @AUTHOR sdyt_zhangjunkai
     */
    public ExcelDataBuilder setAliasHeaders(Map<String, String> headerMapping) {
        excelData.headerMapping = headerMapping;
        return this;
    }

    /**
     * @param rowHeaderMapping 映射：原列头—列头
     * @METHOD_NAME: setAliasRowHeaders
     * @DESCRIPTION: 设置行头别名
     * @CREATE_TIME 2020/9/4 14:49
     * @RETURN void
     * @AUTHOR sdyt_zhangjunkai
     */
    public ExcelDataBuilder setAliasRowHeaders(Map<String, String> rowHeaderMapping) {
        excelData.rowHeaderMapping = rowHeaderMapping;
        return this;
    }

    public ExcelDataBuilder setDefaultStyle(XSSFCellStyle cellStyle) {
        excelData.defaultCellStyle = cellStyle;
        return this;
    }

    public ExcelDataBuilder setRowStyle(int index, XSSFCellStyle style) {
        tmpRow(index).setStyle(style);
        return this;
    }


    public ExcelDataBuilder setColumnStyle(int index, XSSFCellStyle style) {
        tmpColumn(index).setStyle(style);
        return this;
    }

    protected long getCoordinate(int rowIndex, int columnIndex) {
        if (rowIndex < 0 || columnIndex < 0) {
            throw new IllegalArgumentException(MessageFormat.format("Wrong coordinate: [{0},{1}]", rowIndex, columnIndex));
        }
        long hi = 0;
        if (rowIndex > 0) {
            hi = ((long) rowIndex) << 32;
        }
        return hi | columnIndex;
    }

    public ExcelDataBuilder setAutoSizeColumn(boolean autoSizeColumn) {
        excelData.setAutoSizeColumn(autoSizeColumn);
        return this;
    }


    public ExcelDataBuilder setHeaderValueMapping(Map<String, String> headerValueMapping) {
        excelData.headerValueMapping = headerValueMapping;
        return this;
    }

    public ExcelDataBuilder setRowHeaderValueMapping(Map<String, String> rowHeaderValueMapping) {
        excelData.rowHeaderValueMapping = rowHeaderValueMapping;
        return this;
    }


    public boolean isMergeRepeat() {
        return mergeRepeat;
    }

    public ExcelDataBuilder setMergeRepeat(boolean mergeRepeat) {
        this.mergeRepeat = mergeRepeat;
        return this;
    }

    protected SheetRow row(String rowHeader) {
        SheetRow sheetRow = uniqueRows.get(rowHeader);
        if (sheetRow == null) {
            int rowIndex = excelData.firstDataRowIndex + excelData.sheetRows.size() - 1;
            sheetRow = row(rowIndex, rowHeader);
        }
        return sheetRow;
    }

    protected SheetColumn column(String header) {
        SheetColumn sheetColumn = uniqueHeaders.get(header);
        if (sheetColumn == null) {
            int columnIndex = excelData.firstDataColumnIndex + excelData.sheetColumns.size() - 1;
            sheetColumn = column(columnIndex, header);
        }
        return sheetColumn;
    }

    public SheetRow insertRow(int rowIndex, Object pojo) {
        SheetRow sheetRow = tmpRow(rowIndex);
        sheetRow.setPojo(pojo);
        return sheetRow;
    }

    public SheetRow insertRow(int rowIndex, Object pojo, XSSFCellStyle style) {
        SheetRow sheetRow = insertRow(rowIndex, pojo);
        sheetRow.setStyle(style);
        return sheetRow;
    }

    public SheetColumn insertColumn(int columnIndex, Object pojo) {
        SheetColumn sheetColumn = tmpColumn(columnIndex);
        sheetColumn.setPojo(pojo);
        return sheetColumn;
    }

    public ExcelDataBuilder insertColumn(int columnIndex, Object pojo, XSSFCellStyle style) {
        SheetColumn sheetColumn = insertColumn(columnIndex, pojo);
        sheetColumn.setStyle(style);
        return this;
    }

    protected Object getMappedValue(Object data, String property) {
        if (excelData.headerValueMapping != null) {
            property = excelData.headerValueMapping.get(property);
        } else if (excelData.rowHeaderValueMapping != null) {
            property = excelData.rowHeaderValueMapping.get(property);
        }
        //表数据
        return getPropertyWithConvertNull(data, property);
    }


    public boolean isSortRow() {
        return sortRow;
    }

    public boolean isSortColumn() {
        return sortColumn;
    }

    public Map<String, SheetRow> getUniqueRows() {
        return uniqueRows;
    }


    public Map<String, SheetColumn> getUniqueHeaders() {
        return uniqueHeaders;
    }


    public Map<Integer, SheetColumn> getSheetColumns() {
        return excelData.sheetColumns;
    }

    public Map<Integer, SheetRow> getSheetRows() {
        return excelData.sheetRows;
    }

    public boolean existRow(int index) {
        return excelData.sheetRows.containsKey(index);
    }

    public boolean existRow(String shownRowHeader) {
        return uniqueRows.containsKey(shownRowHeader);
    }

    public boolean existColumn(int index) {
        return excelData.sheetColumns.containsKey(index);
    }

    public boolean existColumn(String shownHeader) {
        return uniqueHeaders.containsKey(shownHeader);
    }

    protected SheetColumn tmpColumn(int index) {
        SheetColumn sheetColumn = tmpColumns.get(index);
        if (sheetColumn == null) {
            sheetColumn = new SheetColumn(index);
        }
        tmpColumns.put(index, sheetColumn);
        return sheetColumn;
    }

    protected SheetRow tmpRow(int index) {
        SheetRow row = tmpRows.get(index);
        if (row == null) {
            row = new SheetRow(index);
        }
        tmpRows.put(index, row);
        return row;
    }

}
